package com.zyl.server;

import com.zyl.backup.BackUp;
import com.zyl.bean.Environment;
import com.zyl.dbStore.DbStore;
import com.zyl.factory.FactoryInstance;
import com.zyl.log.Log;
import com.zyl.log.LogImpl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @version v1.0
 * @ProjectName: env_gather
 * @Package: com.zyl.server
 * @ClassName: ServerImpl
 * @Description TODO(服务端接口实现类)
 * @Author 翟玉龙
 * @Date 2024/10/21-10:30
 */
public class ServerImpl implements Server {
    // 日志模块的实现类对象
    private final Log log = LogImpl.getInstance();

    private final ServerSocket server;
    private final ServerSocket stopServer; // 用于监听关闭服务器信号的服务器
    private final ThreadPoolExecutor pool;
    // volatile 让局部变量在线程之间修改可见性
    private volatile boolean flag = true; // 用于控制循环，让服务器一直接收客户端数据
    // 入库模块的对象
    private final DbStore dbStore = FactoryInstance.init().getInstance("dbStore", DbStore.class);
    //private final DbStore dbStore = new DbStoreImpl();

    // 备份模块的实现类对象
    private final BackUp backup = FactoryInstance.init().getInstance("backup", BackUp.class);
    //private final BackUp backup = new BackUpImpl();
    private static final String backUpFile = "backup/dbStore.backup";


    @SuppressWarnings("all")
    public ServerImpl() throws Exception {
        // 创建服务器对象
        server = new ServerSocket(8088);
        // 创建线程池 设定最多可以支持30个任务 最大线程为15
        pool = new ThreadPoolExecutor(11, 15, 2,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(15));
        log.debug("服务器已启动，等待客户端连接....");
        stopServer = new ServerSocket(9999);
    }

    @SuppressWarnings("all")
    private List<Environment> hanldClient(Socket socket) throws Exception {
        log.debug("接收到客户端的连接:" + socket);
        try (ObjectInputStream ois = new ObjectInputStream(
                new BufferedInputStream(socket.getInputStream()))) {
            // 接收客户端传输的数据
            List<Environment> list = (List<Environment>) ois.readObject();
            log.info("本次接收的环境数据条数为: " + list.size());
            return list;
        } finally {
            // 关闭资源
            if(socket != null) {
                socket.close();
            }
        }
    }

    @SuppressWarnings("all")
    public void receive() throws Exception {
        log.debug("开始准备接收客户端数据.......");
        // 分配一个线程，用于监听关闭服务器
        pool.execute(() -> {
            try {
                close();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        while (flag) {
            // 等待客户端连接 当没有客户端连接时 会一直处于阻塞状态
            Socket socket = server.accept();
            // 通过创建多线程的方式，给每一个连接的客户端分配一个线程用于接收处理来自客户端的数据
            Runnable task = () -> {
                List<Environment> list = null;
                try {
                    // 接收数据，需要和客户端保持连接
                    list = hanldClient(socket);
                    // 在入库前，读取备份文件中的数据，将其加入到集合中，统一入库
                    if(new File(backUpFile).exists()) {
                        List<Environment> data = backup.load(backUpFile, List.class, true);
                        // 将数据添加到list中，将其添加到后续入库数据的前面
                        list.addAll(0, data);
                    }
                    // 将环境数据入库，由服务器单独完成，无需客户端保持连接
                    dbStore.dbStore(list);
                } catch (Exception e) {
                    log.error(String.format("服务器出现异常: 【%s】准备数据备份：" , e.getMessage()));
                    // 如果在调用入库方法时报错，即入库前报错需要将所有数据都备份
                    if (list != null && !list.isEmpty()) {
                        try {
                            // 备份数据
                            backup.store(backUpFile, list, false);
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                    throw new RuntimeException(e);
                }
            };
            // 将任务交给线程池分配
            pool.execute(task);
        }
    }

    @Override
    public void close() throws Exception {
        log.debug("监听器已启动，开始监听关闭资源服务器的信号");
        // 设置一个监听者，当监听到某种信号，将flag改为false，关闭服务器以及线程池
        stopServer.accept();
        // 当存在客户端连接stopServer时候,说明需要关闭接收资源的服务器,即执行以下代码
        flag = false;
        // 关闭服务器
        server.close();
        // 关闭线程池 使用shutdown()关闭线程池会在所有任务都执行完毕后再关闭
        pool.shutdown();
        log.warn("服务器已经关闭");
    }
}
